package tester.utilities;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import tester.*;


/**
 * Ideally, this class will be used to create threaded tests that
 * can execute sequentially.  Unfortunately, the current limitations of
 * Cobertura prevent us from running non-synchronous tests.  
 * @author Weston Jossey, Shayne Caswell
 * @since December 12 2008
 * @version 2.0
 */
public class AnnotatedTest implements Runnable{
    String name;
    Object o = null;
    
    public AnnotatedTest(String name){
        this.name = name;
        init();
    }
    
    private void init() {
        Class<?> examples;
        Constructor<?> constructor;
        ConstructorTypes types;
        ConstructorArguments args;
        Class<?>[] argtypes;
        String[] argvals;
        try {
            examples = Class.forName(name);
            types = examples.getAnnotation(ConstructorTypes.class);
            args = examples.getAnnotation(ConstructorArguments.class);
            
            //Both have to exist or we try the default constructor
            if(types == null || args == null){
                constructor = examples.getDeclaredConstructor();
                constructor.setAccessible(true);
                o = constructor.newInstance();
            } else{
                //Grab the values from the annotations
                argtypes = types.argtypes();
                argvals = args.argvals();
                
                //Get the constructor by argtypes.
                //Set it to be accessible
                //Get the arguments
                //Call the constructor
                constructor = examples.getDeclaredConstructor(argtypes);
                constructor.setAccessible(true);
                Object[] finalArgs = createArgumentArray(argvals, argtypes);
                o = constructor.newInstance(finalArgs);
                
            }

            System.out.println("Tester Results");
        } catch (NoSuchMethodException ex) {
            System.out
                    .println("no default constructor: " + ex.getMessage());
        } catch (InvocationTargetException ex) {
            System.out.println("Invocation: " + ex.getMessage());
        } catch (ClassNotFoundException ex) {

        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e){
            System.err.println("Too few values for constructor arguments.");
        }
    }
    
    public Object[] createArgumentArray(String[] argvals,
                                        Class<?>[] argtypes){
        //Construct with pre-defined length
        ArrayList<Object> finalArgs = new ArrayList<Object>(argvals.length);
        //For each argument
        int i = 0;
        if(argvals.length > 0){
            //For each value, parse it an store it in the ArrayList
            for(String value : argvals){
                String argtypename = argtypes[i].getName();
                if(argtypename.equals("int")){
                    finalArgs.add(Integer.parseInt(value));
                } else if(argtypename.equals("double")){
                    finalArgs.add(Double.parseDouble(value));
                } else if(argtypename.equals("boolean")){
                    finalArgs.add(Boolean.parseBoolean(value));
                } else if(argtypename.equals("short")){
                    finalArgs.add(Short.parseShort(value));
                } else if(argtypename.equals("long")){
                    finalArgs.add(Long.parseLong(value));
                } else if(argtypename.equals("byte")){
                    finalArgs.add(Byte.parseByte(value));
                } else if(argtypename.equals("float")){
                    finalArgs.add(Float.parseFloat(value));
                } else if(argtypename.equals("char")){
                    finalArgs.add(value.charAt(0));
                }else{
                    finalArgs.add(argtypes[i].cast(value));
                }
                
                i++;
            }
            
        }
        
        return finalArgs.toArray();
    }

    public void run() {
        // run tests if the instance was successfully constructed
        Tester t = new Tester();
        if (o != null) {
            t.runAnyTests(o);
        }
    }
    
}
